/* 
* Class name: GameBoard *
* 
* Version info: v4.0 *
* 
* Description: Handles all games logic and screen operations
* 
* Copyright by Group4 from CmpE450 course *
*/ 


package com.GameSpace.SameGame;

import com.GameSpace.CoreAssets.BoardSize;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.media.MediaPlayer;
import android.os.Vibrator;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;

import com.GameSpace.CoreAssets.Board;
import com.GameSpace.CoreAssets.Color;
import com.GameSpace.CoreAssets.Coordinate;
import com.GameSpace.CoreAssets.Grid;
import com.GameSpace.SameGame.R;
import com.GameSpace.CoreAssets.SoundManager;


public class GameBoard extends Board {
	
	//The scores in all steps 
	public static int score =0;
	public static int chainScore=0;
	public static int previousScore=0;
	public static int previousChainScore=0;
	
	private static final String logtag = "GameBoard";
	Context ctx;
	
	//The ball arrays which are displayed on the game screen
	private Ball ballArray[] = new Ball[72];
	private Ball previousBallArray[] = new Ball[72];
	private float x;
	private float gridHeight;
	private float gridWidth;
	private int chainPerimeterPoint[] = new int[43];
	//The level of the game
	private int difficulty;
	//true if the game is finished, otherwise false
	private boolean isFinished = false;
	
	//the button views on the game screen
	private View undoBtn;
	
	//the points shown on the game screen
	private TextView scoreBoard;
	private TextView chainBoard;
	
	//container classes to play sounds
	private MediaPlayer mp;
	private MediaPlayer mp2;
	
	//bitmap objects which will be drawn on the screen according to color information of balls
	Bitmap greenBitmap;
	Bitmap yellowBitmap;
	Bitmap redBitmap;
	Bitmap purpleBitmap;
	Bitmap blueBitmap;
 
	//selected bitmap objects which will be drawn for selected balls
    Bitmap brightGreenBitmap;
    Bitmap brightYellowBitmap;
    Bitmap brightRedBitmap;
    Bitmap brightPurpleBitmap;
    Bitmap brightBlueBitmap;
    Activity activity;
	
	public GameBoard( Context context,Activity act ){
		super(context);
		this.activity = act;
	    init();
	}
	
	//initialization function
	public void init() {
		
		//set the initial values of the objects
		grid = new Grid();
	    coordinate = new Coordinate();
	    ctx = getContext();
	    score = 0;
	    chainScore = 0;
	    previousChainScore = 0;
	    previousScore = 0;
	    
	    //create mediaplayer objects to play sounds
	    mp = MediaPlayer.create(ctx, R.raw.freesound);
	    mp2 = MediaPlayer.create(ctx, R.raw.applause);
	    
	    //initialize the array of balls which keeps previous state of the balls
	    for(int i=0;i<72;i++)
	    	previousBallArray[i]=new Ball();
	    
	    //initialize the array of balls which keeps current state of the balls
	    for (int i=0; i<72; i++)
	    	ballArray[i] = new Ball();
	    
	    //keeps the chain perimeter information of cells
	    chainPerimeterPoint[2]=6;
	    chainPerimeterPoint[3]=8;
	    chainPerimeterPoint[4]=8;
	    chainPerimeterPoint[5]=10;
	    chainPerimeterPoint[6]=10;
	    chainPerimeterPoint[7]=12;
	    chainPerimeterPoint[8]=12;
	    chainPerimeterPoint[9]=12;
	    chainPerimeterPoint[10]=14;
	    chainPerimeterPoint[11]=14;
	    chainPerimeterPoint[12]=14;
	    chainPerimeterPoint[13]=16;
	    chainPerimeterPoint[14]=16;
	    chainPerimeterPoint[15]=16;
	    chainPerimeterPoint[16]=16;
	    chainPerimeterPoint[17]=18;
	    chainPerimeterPoint[18]=18;
	    chainPerimeterPoint[19]=18;
	    chainPerimeterPoint[20]=18;
	    chainPerimeterPoint[21]=20;
	    chainPerimeterPoint[22]=20;
	    chainPerimeterPoint[23]=20;
	    chainPerimeterPoint[24]=20;
	    chainPerimeterPoint[25]=20;
	    chainPerimeterPoint[26]=22;
	    chainPerimeterPoint[27]=22;
	    chainPerimeterPoint[28]=22;
	    chainPerimeterPoint[29]=22;
	    chainPerimeterPoint[30]=22;
	    chainPerimeterPoint[31]=24;
	    chainPerimeterPoint[32]=24;
	    chainPerimeterPoint[33]=24;
	    chainPerimeterPoint[34]=24;
	    chainPerimeterPoint[35]=24;
	    chainPerimeterPoint[36]=24;
	    chainPerimeterPoint[37]=26;
	    chainPerimeterPoint[38]=26;
	    chainPerimeterPoint[39]=26;
	    chainPerimeterPoint[40]=26;
	    chainPerimeterPoint[41]=26;
	    chainPerimeterPoint[42]=26;
	    
	    //create image bitmaps for balls 
		greenBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.button_seagreen);
	    yellowBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.button_yellow);
	    redBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.button_red);
	    purpleBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.button_purple);
	    blueBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.button_blue);
	    brightGreenBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.seagreen_selected);
	    brightYellowBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.yellow_selected);
	    brightRedBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.red_selected);
	    brightPurpleBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.purple_selected);
	    brightBlueBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.blue_selected);
	}
	
	//the setter of the difficulty level of the game
	public void setDifficulty( int level ){
		difficulty = level;
	}
	
	//saves the current state of the ball array to the previous ball array
	public void setArray( Ball[] arr ){
		ballArray = arr;
		for(int i=0;i<72;i++){
			previousBallArray[i].setColor(ballArray[i].getColor());
			previousBallArray[i].setIsEmpty(ballArray[i].getIsEmpty());
			previousBallArray[i].setIsSelected(ballArray[i].getIsSelected());
		}
	}
	
	//an event handler which is triggered when user touches on the screen
	@Override
	public boolean onTouchEvent( MotionEvent event ){
		int i;
		int counterSquare=0;
		int xCoor=0;
		int yCoor=0;
		int res[];
		int counter=0;
		
		if (event.getAction() != MotionEvent.ACTION_DOWN)
			return super.onTouchEvent(event);
		
		if((int) ((event.getY()) / gridHeight) < 9){
			select((int) (event.getX() / gridWidth),(int) ((event.getY()) / gridHeight));
		    Log.d(logtag, "eventX " + event.getX() + ",eventY: " + event.getY());
		    Log.d(logtag, "onTouchEvent: x " + coordinate.getXCoord() + ", y " + coordinate.getYCoord());
		    
	    	xCoor = this.coordinate.getXCoord();
	    	yCoor = this.coordinate.getYCoord();
	    	
	    	if(ballArray[xCoor+yCoor*8].getIsEmpty()!=true){
		    	res = markChain(xCoor, yCoor);
		    	counter=0;
		    	for(i=0;res[i]>=0;i++)
		    		counter++;
		    	
		    	if(ballArray[res[0]].getIsSelected()==true){
		    
			    	if(counter>1){
			    		
			    		saveCurrentArray();
			    		for(i=0;res[i]>=0;i++)
			    			ballArray[res[i]].setIsEmpty(true);
			    		
			    		correctArray();
			    		correctColumns();
			    		
			    		score += chainScore;
			    		chainScore=0;
			    		for(i=0;i<72;i++){
			    			ballArray[i].setIsSelected(false);
			    		}
			    		if(SoundManager.isSoundsEnabled())
			    			mp.start();
			    		if(VibrationManager.getVibrationEnabled())
			    			((Vibrator)ctx.getSystemService(ctx.VIBRATOR_SERVICE)).vibrate(300);
			    	}
		    	}
		    	else{
		    		saveCurrentArray();
		    		for(i=0;i<72;i++){
		    			if(ballArray[i].getIsSelected()==true){
		    				ballArray[i].setColor(ballArray[i].getColor()-5);
		    			}
		    			ballArray[i].setIsSelected(false);
		    		}
		    		if(counter>1){
		    			counterSquare = counter*counter;
		    			chainScore = counterSquare + counterSquare*chainPerimeterPoint[counter]/calculatePerimeterChain(res);
		    			chainScore *= difficulty;
			    		for(i=0;res[i]>=0;i++){
			    			ballArray[res[i]].setIsSelected(true);
			    			ballArray[res[i]].setColor(ballArray[res[i]].getColor()+5);
			    		}
		    		}
		    		
		    	}
		    	
	    	}
		}
    	return true;
	}
	
	//refills the empty columns and rows of the current state of balls after an elimination
	public void correctArray(){
		int tempLoc=0;
		int flag=0;
		for(int i=63; i>=0; i--){
			if(ballArray[i].getIsEmpty()==false){
				tempLoc=i;
				flag=0;
				
				while( tempLoc<=63 ){
					if( ballArray[tempLoc+8].getIsEmpty()==true ){
						tempLoc+=8;
						flag=1;
					}	
					else{
						break;
					}
				}
				if(flag==1){
					ballArray[tempLoc].setIsEmpty(false);
					ballArray[tempLoc].setColor(ballArray[i].getColor());
					ballArray[i].setIsEmpty(true);
				}
			}
		}
	}
	
	//fills the empty columns after elimination of balls
	public void correctColumns(){
		boolean isEmpty=true;
		boolean flag=false;
		int j=0,columnNo=0;
		for(int i=0;i<7;i++){
			isEmpty = columnIsEmpty(i);
			j=64+i+1;
			flag=false;
			if(isEmpty){
				while(j<72){
					if(ballArray[j].getIsEmpty()==false){
						flag=true;
						break;
					}
					j++;
				}
				if(flag){
					columnNo = j-64;
					for(int k=0;k<9;k++){
						ballArray[k*8+i].setColor(ballArray[k*8+columnNo].getColor());
						ballArray[k*8+i].setIsEmpty(ballArray[k*8+columnNo].getIsEmpty());
						ballArray[k*8+columnNo].setIsEmpty(true);
					}
				}
				
			}
			
		}
	}
	
	//calculates the score of the user according to predefined perimeter scores and given chain
	public int calculatePerimeterChain( int []arr ){
		int i;
		int counter=0;
		int j;
		int result=0;
		for(i=0;arr[i]>=0;i++){
			counter=0;
			if(arr[i]>=8){
				for(j=0;arr[j]>=0;j++){
					if((arr[i]-8)==arr[j]){
							break;
					}
				}
				if(arr[j]>=0)
					counter++;
			}
			if(arr[i]<64){
				for(j=0;arr[j]>=0;j++){
					if((arr[i]+8)==arr[j]){
							break;
					}
				}
				if(arr[j]>=0)
					counter++;
			}
			if(arr[i]%8 > 0 ){
				for(j=0;arr[j]>=0;j++){
					if((arr[i]-1)==arr[j]){
							break;
					}
				}
				if(arr[j]>=0)
					counter++;
			}
			if(arr[i]%8 <7){
				for(j=0;arr[j]>=0;j++){
					if((arr[i]+1)==arr[j]){
							break;
					}
					
				}
				if(arr[j]>=0)
					counter++;
			}
			result+=4-counter;
			
		}
		return result;
	}
	
	//returns true if all the balls in a column is not empty
	public boolean columnIsFull( int columnNo ){
		for(int j=0;j<9;j++){
			if(ballArray[j*8+columnNo].getIsEmpty()==true){
				return false;
			}
		}
		return true;
	}
	
	//returns true if all the balls in a column is not empty
	public boolean columnIsEmpty( int columnNo ){
		for(int j=0;j<9;j++){
			if(ballArray[j*8+columnNo].getIsEmpty()==false){
				return false;
			}
		}
		return true;
	}
	
	//keyboard event handler which is triggered when user pushes one of the keys on the phone
	@Override
	public boolean onKeyDown( int keyCode, KeyEvent event ){
		Log.d(logtag, "onKeyDown: keycode=" + keyCode + ", event=" + event);
	    switch (keyCode) {
	    case KeyEvent.KEYCODE_DPAD_UP:
	    	select(coordinate.getXCoord(), coordinate.getYCoord() - 1);
	    break;
	    case KeyEvent.KEYCODE_DPAD_DOWN:
	    	select(coordinate.getXCoord(), coordinate.getYCoord() + 1);
	    break;
	    case KeyEvent.KEYCODE_DPAD_LEFT:
	    	select(coordinate.getXCoord() - 1, coordinate.getYCoord());
	    break;
	    case KeyEvent.KEYCODE_DPAD_RIGHT:
	         select(coordinate.getXCoord() + 1, coordinate.getXCoord());
	    break;
	    case KeyEvent.KEYCODE_MENU:
	    	showGameMenu();
	    break;
	    default:
	    return super.onKeyDown(keyCode, event);
	    }
	    return true;
	}
	
	//set the undo button
	public void setUndoButton( View undoBtn ){
		this.undoBtn = undoBtn;
		
		this.undoBtn.setOnClickListener(new OnClickListener() {
        	public void onClick(View v) {
        		restorePreviousArray();
        	}
        });
	}
	
	//shows game menu
	public void showGameMenu() {
		Intent startGameIntent = new Intent(ctx, GameSpace.class);
    	ctx.startActivity(startGameIntent);
	}
	
	//set the view object which will show the score of the user
	public void setScoreBoard( View board ){
		scoreBoard = (TextView)board;
	}
	
	//set the view object which will show the chain score of the user
	public void setChainBoard( View board ){
		chainBoard = (TextView)board;
	}
	
	//return true if all the balls is eliminated; otherwise false
	public boolean allBallsEliminated(){
		for(int i=0;i<72;i++){
			if(ballArray[i].getIsEmpty()==false)
				return false;
		}
		return true;
	}
	
	//selects a chain according to given coordinates of the first selected ball
	public int[] markChain( int xCoor, int yCoor ){
		int arr[]=new int[72];
		int resultArr[]=new int[72];
		int flag=0;
		int index=1;
		int currentBall=0;
		int resIndex=1;
		int i;
		int j;
		int k;
		
		for(k=0;k<72;k++){
			arr[k]=-1;
			resultArr[k]=-1;
		}
		
		arr[0] = xCoor + yCoor*8;
		
		resultArr[0] = arr[0];
		
		while(index>0){
			currentBall = arr[0];
			if(currentBall>=8 && ballArray[currentBall-8].getColor()==ballArray[currentBall].getColor() && ballArray[currentBall-8].getIsEmpty()!=true){
				
				for( j=0; resultArr[j]>=0; j++){
					if(resultArr[j]==currentBall-8){
						flag=0;
						break;
					}
					flag=1;
				}
				
				if(flag==1){
					resultArr[resIndex] =  currentBall-8;
					resIndex++;
					arr[index]= currentBall-8;
					index++;
				}
			}
			
			if(currentBall<64 && ballArray[currentBall+8].getColor()==ballArray[currentBall].getColor() && ballArray[currentBall+8].getIsEmpty()!=true){
				for( j=0; resultArr[j]>=0;j++){
					if(resultArr[j]==currentBall+8){
						flag=0;
						break;
					}
					flag=1;
				}
				if(flag==1){
					resultArr[resIndex] =  currentBall+8;
					resIndex++;
					arr[index]= currentBall+8;
					index++;
				}
			}
			if(currentBall%8 > 0 && ballArray[currentBall-1].getColor()==ballArray[currentBall].getColor() && ballArray[currentBall-1].getIsEmpty()!=true){
				for( j=0; resultArr[j]>=0;j++){
					if(resultArr[j]==currentBall-1){
						flag=0;
						break;
					}
					flag=1;
				}
				if(flag==1){
					resultArr[resIndex] =  currentBall-1;
					resIndex++;
					arr[index]= currentBall-1;
					index++;
				}
			}
			if( ((currentBall%8) < 7) && ballArray[currentBall+1].getColor()==ballArray[currentBall].getColor() && ballArray[currentBall+1].getIsEmpty()!=true){
				for( j=0; resultArr[j]>=0;j++){
					if(resultArr[j]==currentBall+1){
						flag=0;
						break;
					}
					flag=1;
				}
				if(flag==1){
					resultArr[resIndex] =  currentBall+1;
					resIndex++;
					arr[index]= currentBall+1;
					index++;
				}
			}
			for( i=0;i<index-1;i++){
				arr[i]=arr[i+1];
			}
			arr[index-1]=-1;
			index--;
		}
		
		return resultArr;
	}
	
	//shows score of the user on the screen
	public void drawScore( Canvas canvas ){
		
		scoreBoard.setText("Score: "+String.valueOf(score));
		chainBoard.setText("Chain: "+String.valueOf(chainScore));
		
	}
	
	/*
	 * returns true if there is no ball to eliminate; otherwise false
	 * it determines end of game according to possibility of having chain on the screen
	 */
	public boolean isGameFinished(){
		int currentBall=0;
		while(currentBall<72){
			if(ballArray[currentBall].getIsEmpty() == false){
				if(currentBall>8 && ballArray[currentBall-8].getColor()==ballArray[currentBall].getColor()  && ballArray[currentBall-8].getIsEmpty()!=true){
					return false;
				}
				if(currentBall<64 && ballArray[currentBall+8].getColor()==ballArray[currentBall].getColor() && ballArray[currentBall+8].getIsEmpty()!=true){
					return false;
				}
				if(currentBall%8 > 0 && ballArray[currentBall-1].getColor()==ballArray[currentBall].getColor()  && ballArray[currentBall-1].getIsEmpty()!=true){
					return false;
				}
				if(currentBall%8 < 7 && ballArray[currentBall+1].getColor()==ballArray[currentBall].getColor()  && ballArray[currentBall+1].getIsEmpty()!=true){
					return false;
				}
			}
			currentBall++;
		}
		
		//if sound is enabled play the end game sound
		if(SoundManager.isSoundsEnabled()){
			if(mp.isPlaying())
    			mp.stop();
    		mp2.start();
		}
		
		return true;
	}

	//overridden method which is fired when size of the screen is changed
	@Override
	protected void onSizeChanged( int w, int h, int oldw, int oldh ){
		grid.setWidth(w / (float)BoardSize.getSizeX());
	    grid.setHeight(h / (float)BoardSize.getSizeY());
	    getSelectedGridRectangle(coordinate.getXCoord(), coordinate.getYCoord(), selRect);
	    Log.d(logtag, "onSizeChanged: width " +  grid.getWidth() + ", height " + grid.getHeight());
	    super.onSizeChanged(w, h, oldw, oldh);
	}
	
	/*
	 * overridden method which renders the screen objects
	 * it renders or does not render balls according to their colors and empty values
	 */
	@Override	
    protected void onDraw( Canvas canvas ){
	    //drawing background
		int color;
		int i;
		int j;
	    int counter =0;
	    Paint paint;
		Paint foreground= new Paint(Paint.ANTI_ALIAS_FLAG);
	    Paint background = new Paint();
	    background.setColor(Color.getBackgroundColor());
	    canvas.drawRect(0, 0, getWidth(), getHeight(), background);
	    
	    //writes the score of the user on the screen
	    drawScore(canvas);
	    
	    //foreground draw properties
	    foreground.setColor(Color.getForegroundColor());
	    foreground.setStyle(Style.FILL);
	    foreground.setTextSize(grid.getHeight() * 0.75f);
	    foreground.setTextScaleX(grid.getWidth() / grid.getHeight());
	    foreground.setTextAlign(Paint.Align.CENTER);
	    
	    x = grid.getWidth() / 3 - 8;
	    gridWidth = grid.getWidth();
	    gridHeight = grid.getHeight();
	    
	   
	    for ( j = 0; j <BoardSize.getSizeY(); j++) {
	    	for ( i = 0; i < BoardSize.getSizeX(); i++) {
	    		
	    		//if the ball is not empty draw it on the screen
	    		if (ballArray[i+j*8].getIsEmpty()==false) {
	    			counter = i+j*8;
		    		paint = new Paint();
		    		color = ballArray[counter].getColor();
		    		
		    		//determine color properties of the balls according to given color index values
		    		if(color==0 || color==5)
		    			paint.setColor(getResources().getColor(R.color.blue));
		    		else if(color==1 || color==6)
		    			paint.setColor(getResources().getColor(R.color.red));
		    		else if(color==2 || color==7)
		    			paint.setColor(getResources().getColor(R.color.green));
		    		else if(color==3 || color==8)
		    			paint.setColor(getResources().getColor(R.color.purple));
		    		else if(color==4 || color==9)
		    			paint.setColor(getResources().getColor(R.color.yellow));
		    		
		    		//according to color indexes draw balls on the screen
		    		if(color == 0)
		    			canvas.drawBitmap(blueBitmap, i*gridWidth + x,  j * gridHeight  , paint);
		    		else if(color == 1)
		    			canvas.drawBitmap(yellowBitmap, i*gridWidth + x,  j * gridHeight   , paint);
		    		else if(color == 2)
		    			canvas.drawBitmap(purpleBitmap, i*gridWidth + x,  j * gridHeight , paint);
		    		else if(color == 3)
		    			canvas.drawBitmap(greenBitmap, i*gridWidth + x,  j * gridHeight   , paint);
		    		else if(color == 4)
		    			canvas.drawBitmap(redBitmap, i*gridWidth + x,  j * gridHeight   , paint);
		    		else if(color == 5)
		    			canvas.drawBitmap(brightBlueBitmap, i*gridWidth + x,  j * gridHeight  , paint);
		    		else if(color == 6)
		    			canvas.drawBitmap(brightYellowBitmap, i*gridWidth + x,  j * gridHeight  , paint);
		    		else if(color == 7)
		    			canvas.drawBitmap(brightPurpleBitmap, i*gridWidth + x,  j * gridHeight   , paint);
		    		else if(color == 8)
		    			canvas.drawBitmap(brightGreenBitmap, i*gridWidth + x,  j * gridHeight   , paint);
		    		else if(color == 9)
		    			canvas.drawBitmap(brightRedBitmap, i*gridWidth + x,  j * gridHeight   , paint);
		    				    		
	    		}
	        }
	    }
	    
	    //if game is already finished do not check whether or not game is finished
	    if (!isFinished) {
		    
	    	//check is if game is finished
		    if (isGameFinished()) {
		    	//if all balls are eliminated, assign bonus score for the user
		    	if(allBallsEliminated()){
		    		EndGame.bonus = difficulty * 100;
		    	}
		    	else{
		    		EndGame.bonus = 0;
		    	}
		    	
		    	//set the score of the user
		    	EndGame.score = score + EndGame.bonus;
		    	isFinished = true;
		    	
		    	//call the end game class
		    	Intent endGameIntent = new Intent(ctx, EndGame.class);
		    	ctx.startActivity(endGameIntent);
		    	activity.finish();
		    }
		    else {
		    	invalidate();
		    }
	    }
	}
	
	//save current array to the previous array
	private void saveCurrentArray(){
		for(int i=0;i<72;i++){
			previousBallArray[i].setColor(ballArray[i].getColor());
			previousBallArray[i].setIsEmpty(ballArray[i].getIsEmpty());
			previousBallArray[i].setIsSelected(ballArray[i].getIsSelected());
		}
		previousChainScore = chainScore;
		previousScore = score;
	}
	
	//restores the current ball of array from the previous ball array when user wants to undo
	private void restorePreviousArray(){
		for(int i=0;i<72;i++){
			ballArray[i].setColor(previousBallArray[i].getColor());
			ballArray[i].setIsEmpty(previousBallArray[i].getIsEmpty());
			ballArray[i].setIsSelected(previousBallArray[i].getIsSelected());
		}
		chainScore = previousChainScore;
		score = previousScore;
	}
}
